<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="author" content="mayouchen" />
    <link rel="Shortcut Icon" href="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1517820809119&di=91454088d1a6598a03ed5527086eeb41&imgtype=0&src=http%3A%2F%2Fm.qqzhi.com%2Fupload%2Fimg_2_1012068656D810419868_23.jpg" />
    <meta content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
    <title>ES6实践</title>
</head>
<style type="text/css">
    @charset "utf-8";
    *{margin:0;padding:0;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);-webkit-box-sizing: border-box;-moz-box-sizing: border-box;}

    html {
        /*height:100%;*/
        min-height: 100%;
        font-size: 100%;
        -webkit-text-size-adjust: 100%;
        -ms-text-size-adjust: 100%;
    }


    body {
        width: 100%;
        /*height:100%;*/
        min-height: 100%;
        font-family:"Microsoft YaHei","微软雅黑","MicrosoftJhengHei","华文细黑","Helvetica", "Arial", "sans-serif";
        font-size: 14px;
        position: relative;
        word-break:break-all;
    }

    a {
        text-decoration: none;
        -webkit-tap-highlight-color: rgba(0, 0, 0, 0.35);
        -webkit-box-sizing:border-box;
    }

    img {
        -ms-interpolation-mode: bicubic;
        vertical-align: middle;
    }

    img:not([src*="/"]) {
        display: none;
    }

    table {
        border-collapse: collapse;
        border-spacing: 0;
    }
    textarea {
        resize: none;
    }

    input, button, select, textarea {
        -webkit-appearance:none;
        outline: none;
        border-radius: 0;
    }
    input::-webkit-outer-spin-button,
    input::-webkit-inner-spin-button {
        -webkit-appearance: none !important;
        margin: 0;
    }
    ul,ol,li {
        list-style: none;
        -webkit-margin-before: 0;
        -webkit-margin-after: 0;
        -webkit-margin-start: 0;
        -webkit-margin-end: 0;
        -webkit-padding-start: 0;
    }
    .section-body .text  {
        margin-top: 24px;
        margin-left: 40px;
        margin-right: 35px;
        width: 84%;
        font-family: "宋体";
        font-size: 18px;
    }
    .section-body .textImg{
        margin-top: 45px;
        color: blue;
        text-align: center;
        vertical-align: middle;
    }
</style>
<script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>
<!-- <script src="../module/music.js"></script> -->
<script type="application/javascript">
    window.onload = function(){
        fuc1();
        console.warn("let");
        fuc2();
        console.warn("const");
        fuc3();
        console.warn("构数组赋值，函数返回数组");
        fuc4();
        console.warn("解构对象赋值，函数返回对象");
        fuc5();
        console.warn("字符模板");
        fuc6();
        console.warn("带有标签的字符模板,标签其实是一个函数---常用于在字符模板中添加HTML标签使用");
        fuc7(); 
        console.warn("判断一个字符串中是否含有另一个字符串，字符创重复，（ES2017 引入了字符串补全长度的功能）");
        fuc8();
        console.warn("设置一个函数的默认值，如果对函数没有传参数的话，就使用默认值做参数");
        fuc9();
        console.warn("展开操作符");
        fuc10();
        console.warn("展开操作符2，在参数中传递的时候");
        fuc11();
        console.warn("结构对象，为函数设置对象的参数"); 
        fuc12();
        console.warn("获取函数的名字"); 
        fuc13();
        console.warn("使用对象表达式");  
        fuc14();
        console.warn("使用对象de属相名  解决属性中包含空格的问题");  
        fuc15();
        console.warn("-0 +0 NaN 特殊情况判断是否相等('==','==='显示错误)");
        fuc16();
        console.warn("Object.assign() 把一个对象的值复制到另一个对象里面去"); 
        fuc17();
        console.warn("Object.setPrototypeOf() 在创建对象的时候改变对象的Prototype");  
        fuc18();
        console.warn("__proto__来设置对象的Prototype");  
        fuc19();
        console.warn("super 用来获取到原来Prototype对象的方法和属性值以后再进行一次自己的修改");
        fuc20();
        console.warn("Iterator 迭代器 （轮流交换数据）{value:'XXXX',down:'true/false'} next返回一个对象");
        fuc21();
        console.warn("定义一个Class类");
        fuc22();
        console.warn("定义一个Class类 里面的get 和 set 方法");
        fuc23();
        console.warn("static 不需要实例化class, 就可以直接使用的方法");
        fuc24();  
        console.warn("extends  实现一个类继承另一个类");
        fuc25();
        console.warn("Set 用于存储数据，不能包含重复的数据");
        fuc26();  
        console.warn("Map 用于存储数据，专门存储明名值对的数据");
        fuc27();  
        console.warn("Model模块，根据应用的需要把它分割 成各个部分,通过export暴露，通过import导入");
        fun28();
        console.warn("箭头函数");
        fun29();
        console.warn("双冒号运算符~");
        fuc30();
        console.warn("1-Array.from方法用于将两类对象转为真正的数组：类似数组的对象（array-like object）和可遍历（iterable）的对象(Set 和 Map)     2-Array.of() 将一组值，转换为数组");
        fuc31();
        console.warn("Promise对象");
        fuc32();
        console.warn("async 函数");  
    }

    var fuc1 = () =>{  //let作用域    let完全可以取代var，因为两者语义相同，而且let没有副作用。
        {
            let aa = "123";
            console.log(aa);
        }
        /* console.info(aa,"111111111");  aa is notdefine */
    }
    var fuc2 = () =>{  
                                  //const常量    在let和const之间，建议优先使用const，尤其是在全局环境，不应该设置变量，只应设置常量。  
                                  //const比较符合函数式编程思想，运算不改变值，只是新建值，而且这样也有利于将来的分布式运算；
        const arr = []; 
        arr.push("O-O");
        arr.push("O^O");
        console.log(arr);
        /*arr = [];    //Assignment to constant variable.
        console.log(arr);*/
    }
    var fuc3 = () =>{  //解构数组赋值    使用数组成员对变量赋值时，优先使用解构赋值
        let functionX = () =>{
            return ["11","22","333"]
        } 
        //例1   获取到所有的数据
        let [A,B,C] = functionX();
        console.log(A+"--"+B);
        console.log(A+"--"+B+"---"+C);


        //例2    获取到前面的数据
        let [E,F] = functionX();
        console.log(E+"--"+F);   
    }
    var fuc4 = () =>{  //结构对象赋值   1-函数的参数如果是对象的成员，优先使用解构赋值    2-如果函数返回多个值，优先使用对象的解构赋值，而不是数组的解构赋值。
        
        let functionX = () =>{  //这样便于以后添加返回值，以及更改返回值的顺序
            return {
                aa:"111", 
                bb:"222",
                cc:"333"
            }
        } 
        //例1   获取到所有的数据
        let {aa:aa,bb:bb,cc:cc} = functionX();
        console.log(aa+"--"+bb+"---"+cc);

        /*例2  获取对象的部分值  但是现在不能重复声明

        let {bb:bb,cc:cc} = functionX();
        console.log(bb+"---"+cc);*/

    }
    var fuc5 = () =>{  //字符模板
        let [aa,bb] = ["ppp","aaa"];
        let string = aa+"---"+bb;
        let string2 = `${aa}---${bb}+++++
${aa}+++++++ ${aa}
                       ${aa}+++++++ ${aa}`;

        /*使用字符模板可以直接设置换行*/
        console.log(string);
        console.log(string2);

        //常见用途

        //例1-添加一段HTML

        // $('#result').append(`
        //      There are <b>${basket.count}</b> items
        //      in your basket, <em>${basket.onSale}</em>
        //      are on sale!
        // `);

        //例2-模板字符串中使用特殊祝福，则前面要用反斜杠转义

        let greeting = `\`Yo\` \" World!  \'`;
        console.log(greeting);

        //例3-所有模板字符串的空格和换行，都是被保留的，比如<ul>标签前面会有一个换行。如果你不想要这个换行，可以使用trim方法消除它。
        
        // $('#list').html(`
        //     <ul>
        //     <li>first</li>
        //     <li>second</li>
        //     </ul>
        // `.trim());
    }
    var fuc6 = () =>{  //带有标签的字符模板
         

        let eatFruit = (strings,...values) => {  //好像不支持箭头函数
            console.log(strings);
            console.log(values);
        }

        let [aa,bb,cc] = ["apple","banana","pear"];
        let string = eatFruit`今天要吃${aa} \n 和${bb}还有${cc}!`;

       

        // function eatFruit(strings,...values){
        //     console.log("---------------------------");
        //     console.log(strings);
        //     console.log(values);
        //     console.log("---------------------------");

        //     let result =[];
        //     for(let i=0;i<values.length;i++){
        //         result +=strings[i];
        //         result +=values[i];
        //     }
        //     result += strings[strings.length-1];
        //     console.log(result);
        //     return result;
        // }
    }
    var fuc7 = () =>{  //判断一个字符串中是否含有另一个字符串,以及字符串重复，（ES2017 引入了字符串补全长度的功能）
        let [aa,bb,cc] = ["apple","banana","pear"];
        let string = `今天要吃${aa} \n 和${bb}还有${cc}!`;
        //例1   startsWith\endsWith\includes
        console.log(string.startsWith("今天"));
        console.log(string.endsWith("!"));
        console.log(string.includes("和"));
        console.log(aa.repeat(3));

        //例2   ES7  padStart()与padEnd()
                                            //-----------------如果省略第二个参数，默认使用空格补全长度。
                                            //----------------如果原字符串的长度，等于或大于指定的最小长度，则返回原字符串。
                                            //-----两者的长度之和超过了指定的最小长度，则会截去超出位数的补全字符串。
        console.log(bb.padStart(10,'ab')); //如果某个字符串不够指定长度，会在头部补全。padStart()  
        console.log(bb.padEnd(10,'ab'));   //如果某个字符串不够指定长度，会在尾部补全。padEnd()

        //例3  padStart()与padEnd()常用方法：提示字符串格式（例如提示是日期类型）
        console.log('12'.padStart(10, 'YYYY-MM-DD')); // "YYYY-MM-12"
        console.log('09-12'.padStart(10, 'YYYY-MM-DD')); // "YYYY-09-12"
        
    }
    var fuc8 = () =>{  //设置一个函数的默认值
       let eatmeat = (goupes,apple = "red") =>{
            return `${apple}----${goupes}`
       } 
       console.log(eatmeat());
       console.log(eatmeat(undefined,"white"));
       console.log(eatmeat("white","white"));
    }
    var fuc9 = () =>{  //展开操作符
       //例1 数组操作
       let arr = ["ll","kk","jj","zz"];
       let arr2 = ["aa","ss","ff"]; 
       let arr3 = [...arr,...arr2];
       let arr4 = arr.concat(arr2)
       console.log(arr);
       console.log(...arr2);  //(1)直接展开一个数组
       console.log(arr3);     //(2)合并另个数组为一个数组
       console.log(arr4);     //(3)合并另个数组为一个数组 相同的原生方法

       //例2 对象操作（ES7语法支持）
       
       let {x,y,...z}={x:1,y:2,a:3,b:4}; //(1)将一个对象当中的对象的一部分取出来成为一个新对象赋值给展开运算符的参数
       console.log("999999999999999999999999999999999999999",z);

       let m = {a:3,b:4};                //(2)将一个对象当中的对象的一部分取出来成为一个新对象赋值给展开运算符的参数
       let n = {x:1,y:2,...z};
       console.log(n);
                                         //(3)合并两个对象
       let Q = {Q1:"111",Q2:"2222"}
       let M = {M1:"333",M2:"4444"}
       let QM = {...Q,...M}
       console.log(QM);
    }
    var fuc10 = () =>{  //展开操作符2 (展开操作符如果出现在参数，则称之为剩余操作符~)
        let ma = (ma,you,...chen) =>{
            console.log(ma,you,chen);
            console.log(ma,you,...chen);
        }
        console.log(ma("马优晨","大帅哥","你好","啊","哈哈!"));
    }
    var fuc11 = () =>{  //结构对象，为函数设置对象的参数
       let show = (breakfeast,lunch,dinner,{vegeables,meat}={}) =>{
            console.log(breakfeast,lunch,dinner,meat,vegeables);
       }
       show("豆浆&油条","炒菜米饭","稀饭",{meat:"牛肉",vegeables:"青菜"});
    }
    var fuc12 = () =>{  //获取函数的名字
      let func1 = () =>{
       
      }
      let func2 = function(){

      }
      let func3 = function funcX(){

      }
      console.log(func1.name,func2.name,func3.name);
    }
    var fuc13 = () =>{  //使用对象表达式 （省略对于的定义，省略后 1-属性值和属性名一样 2-函数直接省略:function）
      let [ma1,ma2] = ["O-O","O^O"];
      let object1 = { //原始赋值方法
          ma3:ma1,
          ma4:ma2,
          ma5:function(){
           
          }
      }
      console.log(object1);

      let object2 = { //ES6赋值方法
          ma1,
          ma2,
          ma6(){
           
          }
      }
      console.log(object2);
    }
    var fuc14 = () =>{  //使用对象de属性名  解决属性中包含空格的问题

      let drink = {};  //方法一
      drink.tea = "绿茶";
      drink['water melon'] = "大西瓜";
      console.log(drink);

      let drink2 = {};  //方法二
      let melon= "water melon";
      drink2.tea = "绿茶2";
      drink2[melon] = "大西瓜2";
      console.log(drink2);
    }
    var fuc15 = () =>{  //-0 +0 NaN 特殊情况判断是否相等("==","==="显示错误)
     console.log(+0 == -0);
     console.log(+0 === -0);
     console.log(NaN == NaN);
     console.log(Object.is(+0,-0));
     console.log(Object.is(NaN,NaN));
    }
    var fuc16 = () =>{  //Object.assign() 把一个对象的值复制到另一个对象里面去

        let [obj1,obj2]=[  //1- 前后两个对象有重复的属性，后面的会覆盖前面的属性
            {
              name:"马优晨",
              age:"24"
            },
            {
              name:"吴京",
              age:"26"
            }
        ]
        Object.assign(obj1,obj2);
        console.log(obj1);

        let [obj3,obj4]=[   //2- 前后两个对象mei有重复的属性，所有属性都会展示
            {
              name:"马优晨",
              age:"24"
            },
            {
              Sexname:"张雪静",
              Sexage:"24"
            }
        ]
        Object.assign(obj3,obj4);
        console.log(obj3);
    }
    var fuc17 = () =>{  //Object.setPrototypeOf() 
         //在创建对象的时候改变对象的Prototype.(prototype 属性de作用:向对象添加属性和方法)
        let sunnday = {
            eatFruit(){
                return "Apple!"
            }
        }
        let monday = {
            eatMeat(){
                return "人肉!"
            }
        }

        let friday = Object.create(sunnday);
        console.log(friday.eatFruit());
        console.log(Object.getPrototypeOf(friday) === sunnday);

        Object.setPrototypeOf(friday,monday);
        console.log(friday.eatMeat());
        console.log(Object.getPrototypeOf(friday) === monday);
    }
    var fuc18 = () =>{  //__proto__ 来设置对象的Prototype
        let sunnday = {
            eatFruit(){
                return "orange!"
            }
        }
        let monday = {
            eatMeat(){
                return "龙肉!"
            }
        }

         //例1   第一种种设置Prototype

        let days = {     
            __proto__ : sunnday
        }
        console.log(days.eatFruit());
        console.log(Object.getPrototypeOf(days) === sunnday);

        //例2   第二种设置Prototype
        days.__proto__ =  monday; 
        console.log(days.eatMeat());
        console.log(Object.getPrototypeOf(days) === monday);
    }
    var fuc19 = () =>{  //super 用来获取到原来Prototype对象的方法和属性值
                        //以后再进行一次自己的修改
        let sunnday = {
            eatFruit(){
                return "Strawberry!"
            }
        }
        let monday = {
            eatMeat(){
                return "驴肉!"
            }
        }
        
        let days = {      
            __proto__ : sunnday,
            eatFruit(){
                return super.eatFruit()+"很好次喔~~~"
            }
        }
        console.log(days.eatFruit());
        console.log(Object.getPrototypeOf(days) === sunnday);
        
    }
    var fuc20 = () =>{  // Iterator 迭代器 （轮流交换数据）{value:"XXXX",down:"true/false"} next返回一个对象,没有的话{value:undefined,down:false}
        
        let student =  function* (name){ //定义一个生成器
            for(let i=0;i<name.length;i++){
                yield name[i];
            }
        }
        let goodsName = student(["成凯","王齐"]);

        console.log(goodsName.next());    //执行这个迭代器
        console.log(goodsName.next());
        console.log(goodsName.next());
    }
    var fuc21 = () =>{  // 定义一个Class类
        class Animals{
            constructor(fuckDog){  //constructor 基于原始的class类实例化的一个方法，用于初始化数据,这个方法会自动执行
                this.funckDog = fuckDog;
            }
            kill(){
                let self = this;
                console.log(self.funckDog);
            }
        }

        let dog = new Animals("猥琐发育，别浪~");
        dog.kill();
    }
    var fuc22 = () =>{  // 定义一个Class类 里面的get 和 set 方法（这两个方法不适合与低端的浏览器）
        class Animals{
            constructor(fuckDog){  
                this.funckDog = fuckDog;
                this.pigs = [];
            }
            get pig(){
                return this.pigs;
            } 
            set pig(pigName){
                 this.pigs.push(pigName);
            }
            kill(){
                let self = this;
                console.log(self.funckDog);
            }
        }

        let dog = new Animals();
        console.log(dog.pig = "走啊^(*￣(oo)￣)^");
        console.log(dog.pig = "跑啊^(*￣(oo)￣)^");
        console.log(dog.pig = "飞啊^(*￣(oo)￣)^");
        console.log(dog.pig);
    }
    var fuc23 = () =>{  // static 不需要实例化class, 就可以直接使用的方法
        class Animals{
            constructor(fuckDog){  
                this.funckDog = fuckDog;
                this.pigs = [];
            }
            static eatGod(God){
               console.log(God);
            } 
        }
        Animals.eatGod("上帝其实是一个女孩子^-^");
    }
    var fuc24 = () =>{  // extends  实现一个类继承另一个类
        class Animals{
            constructor(fuckDog,fuckPig){  
                this.funckDog = fuckDog;
                this.fuckPig = fuckPig;
            }
            showName(){
                console.log(`你是${this.funckDog}还是${this.fuckPig}？`);
            }
        }
        class 上帝 extends Animals{
            constructor(fuckDog,fuckPig){  
                super(fuckDog,fuckPig);   //super 会执行父类的方法（如果继承的类里面有constructor，则必须使用super方法）
            } 
        }
        let 中国的上帝 = new 上帝("玉皇大帝","如来佛祖");
        中国的上帝.showName();
    }
    var fuc25 = () =>{  // Set  用于存储数据，不能包含重复的数据
        let setFoods = new Set();
        setFoods.add("O-O");
        setFoods.add("^-^");
        setFoods.add('(*^▽^*)');

        console.log(setFoods);
        console.log(setFoods.size);        //获取Set长度
        console.log(setFoods.has("O-O"));  //获取Set 中是否含有某一项
        setFoods.delete("O-O");            //获取Set 删除某一项
        console.log(setFoods);
        setFoods.forEach((value)=>{        //遍历Set
            console.log(value);
        });
        setFoods.clear();                  //清空Set
        console.log(setFoods);

        //使用Set求   两个数组的并集|| 交集||差集
        let a = new Set([1, 2, 3]);
        let b = new Set([4, 3, 2]);

        // 并集
        let union = new Set([...a, ...b]);
        // Set {1, 2, 3, 4}

        // 交集
        let intersect = new Set([...a].filter(x => b.has(x)));
        // set {2, 3}

        // 差集
        let difference = new Set([...a].filter(x => !b.has(x)));
        // Set {1}
    }
    var fuc26 = () =>{  // Map  用于存储数据，专门存储明名值对的数据
        let [foods1,foods2,foods3] = [{},{},"好吃的!"];
        let setFoods = new Map();
        setFoods.set(foods1,"西瓜");
        setFoods.set(foods2,"猕猴桃");
        setFoods.set(foods3,"火龙果");

        console.log(setFoods);
        console.log(setFoods.size);
        console.log(setFoods.has(foods2));
        console.log(setFoods.get(foods3));
       
        setFoods.forEach((value,index)=>{
            console.log(`${value} = ${index}`);
        });
        setFoods.delete(foods2) 


        //2-MAP的正常遍历方式

        const map = new Map([
            ['MAP1', 'MAP_VALUE1'],
            ['MAP2', 'MAP_VALUE2'],
        ]);

        for (let [key, value] of map.entries()) {
            console.log(key, value);
        }

    }
    var fuc27 = () =>{  // Model模块，根据应用的需要把它分割 成各个部分,通过export暴露，通过import导入
        console.log("map");

        /**** 安装cnpm****/
        //npm install --save-dev babel-preset-es2015
        //cnpm install

        /**** 安装webpack****/
        //npm install -g webpack
        //cnpm install -g webpack 接着通过命令行工具定位到你的工程文件的根目录下, 再次安装webpack到你的工程中

        /**** 安装babel****/

        //  ES2015转码规则
        // cnpm install --save-dev babel-preset-es2015

        //  react转码规则
        // npm install --save-dev babel-preset-react

        // ES7不同阶段语法提案的转码规则（共有4个阶段），选装一个
        //  npm install --save-dev babel-preset-stage-0
        //  npm install --save-dev babel-preset-stage-1
        //  npm install --save-dev babel-preset-stage-2  （我选的这个）
        // npm install --save-dev babel-preset-stage-3

        //cnpm install --global babel-cli

        //cnpm install babel
        //cnpm install babel-cli
        //cnpm install babel-core
        //cnpm install babel-loader
        //cnpm install babel-preset-es2015
    }

    var fun28 = () =>{
        //箭头函数的一个用处是简化回调函数
        // 正常函数写法
        let a1 = [1,2,3].map(function (x) {
            return x * x;
        });

        // 箭头函数写法
        let a2 = [1,2,3].map(x => x * x);

        console.log(a1,a2);

    }

    var fun29 = ()=>{
        // 箭头函数可以绑定this对象，大大减少了显式绑定this对象的写法（call、apply、bind）。
        // 但是，箭头函数并不适用于所有场合，所以现在有一个提案，提出了“函数绑定”（function bind）运算符，用来取代call、apply、bind调用。
        //双冒号左边是一个对象，右边是一个函数。该运算符会自动将左边的对象，作为上下文环境（即this对象），绑定到右边的函数上面。
        console.log('::');
        // foo::bar;
        // // 等同于
        // bar.bind(foo);

        // foo::bar(...arguments);
        // // 等同于
        // bar.apply(foo, arguments)
        //
         }
    var fuc30 = () =>{  //arrayLike 和Array.of 

        //例1   Arry.from  将对象转换为数组
        Array.from('hello')// ['h', 'e', 'l', 'l', 'o'];
        console.log(Array.from('hello'));
 
        //例2   Arry.from  将对象转换为数组，接收第二个参数  Array.from还可以接受第二个参数，作用类似于数组的map方法，用来对每个元素进行处理，将处理后的值放入返回的数组。
        
        let arrayLike = [1,2,3,4,5,6];

        Array.from(arrayLike, x => x * x);

        // 等同于
        Array.from(arrayLike).map(x => x * x);


         //例3   Arry.of  将一组对象装换成数组
   
         Array.of(3, 11, 8) // [3,11,8]
         Array.of(3) // [3]
         Array.of(3).length // 1
         console.log( Array.of(3, 11, 8));

    }

    var fuc31 = () =>{
        console.log("promise!");
        //所谓Promise，简单说就是一个容器，里面保存着某个未来才会结束的事件（通常是一个异步操作）的结果。 从语法上说，Promise 是一个对象，从它可以获取异步操作的消息。
        //Promise对象代表一个异步操作，有三种状态：pending（进行中）、fulfilled（已成功）和rejected（已失败）。

        //************例1**************************  promise的使用模板

                                          /*const promise = new Promise(function(resolve, reject) {
                                                // ... 一些逻辑代码
                                                if (" 异步操作成功"){
                                                    resolve(value);
                                                } else {
                                                    reject(error);
                                                }
                                            });

                                            promise.then(function(value) {
                                                // success
                                            }, function(error) {
                                                // failure
                                            }); 
                                            */

        //***************例2****************** resolve函数的参数是另一个 Promise 实例

                                          /*const p1 = new Promise(function (resolve, reject) {
                                            setTimeout(() => reject(new Error('fail')), 3000)
                                            })

                                            const p2 = new Promise(function (resolve, reject) {
                                            setTimeout(() => resolve(p1), 1000)
                                            })

                                            p2
                                            .then(result => console.log(result))
                                            .catch(error => console.log(error))
                                            }
                                            // Error: fail*/

        /*上面代码中，p1是一个 Promise，3 秒之后变为rejected。p2的状态在 1 秒之后改变，resolve方法返回的是p1。由于p2返回的是另一个 Promise，
        导致p2自己的状态无效了，由p1的状态决定p2的状态。所以，后面的then语句都变成针对后者（p1）。又过了 2 秒，p1变为rejected，导致触发catch方法指定的回调函数。*/



        //***************例3**********************  Promise.prototype.catch
                                          /*Promise.prototype.catch方法是.then(null, rejection)   //的别名，用于指定发生错误时的回调函数。

                                            getJSON('/posts.json').then(function(posts) {
                                            // ...
                                            }).catch(function(error) {
                                            // 处理 getJSON 和 前一个回调函数运行时发生的错误
                                            console.log('发生错误！', error);
                                            });*/

        //***************例4****************** Promise.all方法用于将多个 Promise 实例，包装成一个新的 Promise 实例。
        

                                           /* const p = Promise.all([p1, p2, p3]);

                                            上面代码中，Promise.all方法接受一个数组作为参数，p1、p2、p3都是 Promise 实例，如果不是，
                                            就会先调用下面讲到的Promise.resolve方法，将参数转为 Promise 实例

                                            const p3 = new Promise((resolve, reject) => {
                                                resolve('hello');
                                              })
                                              .then(result => result)
                                              .catch(e => e);
                                            
                                              const p4 = new Promise((resolve, reject) => {
                                                throw new Error('报错了');
                                              })
                                              .then(result => result)
                                              .catch(e => e);
                                            
                                              Promise.all([p3, p4])
                                              .then(result => console.log(result))
                                              .catch(e => console.log(e));
                                              // ["hello", Error: 报错了]*/

         //***************例5******************         Promise.resolve()将现有对象转为 Promise 对象

                                            Promise.resolve('foo')
                                            // 等价于
                                            new Promise(resolve => resolve('foo'))
                                            //常用操作

                                            /*const preloadImage = function (path) {
                                               return new Promise(function (resolve, reject) {
                                                const image = new Image();
                                                image.onload  = resolve;
                                                image.onerror = reject;
                                                image.src = path;
                                              });*/

    }
    var fuc32 = () =>{    //async函数
        // async函数返回一个 Promise 对象，可以使用then方法添加回调函数。当函数执行的时候，
        //一旦遇到await就会先返回，等到异步操作完成，再接着执行函数体内后面的语句。async函数返回的 Promise 对象，必须等到内部所有await命令后面的 Promise 对象执行完，才会发生状态改变，
        //除非遇到return语句或者抛出错误。也就是说，只有async函数内部的异步操作执行完，才会执行then方法指定的回调函数。

        // *************************定义方式***********************************************//

       /*
        //*************************函数声明
        async function foo() {}

        // ************************ 函数表达式
        const foo = async function () {};

        //********************* 箭头函数
        const foo = async () => {};
        
        //*************************对象的方法
        let obj = { async foo() {} };
        obj.foo().then(...);

        //*************************Class 的方法
        class Storage {
            constructor() {
                 this.cachePromise = caches.open('avatars');
        }

        async getAvatar(name) {
            const cache = await this.cachePromise;
            return cache.match(`/avatars/${name}.jpg`);
        }
        }

        const storage = new Storage();
        storage.getAvatar('jake').then(…);

        */

        //例1  探究await函数执行的顺序
        function timeout(ms) {
            console.log("step2");
            return new Promise((resolve) => {
              setTimeout(resolve, ms);
              console.log("step3");
            });
        }
          
        async function asyncPrint(value, ms) {
            console.log("step1");
            await timeout(ms);
            console.log("step4");
            console.log(value);
        }
          
        asyncPrint('hello world', 50); 
  
        //例2  探究await函数错误执行机制 
        async function f() {
            throw new Error('出错了');
        }  

        f().then(
            v => console.log(v),
            e => console.log(e)
        )
        // Error: 出错了
        //解释：async函数内部抛出错误，会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。

        //例3       await 后面的promise如果执行失败，则后面的promise都不会在执行，如果我们希望即使前一个异步操作失败，也不要中断后面的异步操作。
        //这时可以将第一个await放在try...catch结构里面，
        //这样不管这个异步操作是否成功，第二个await都会执行
        async function fn() {
            await Promise.reject('出错了')
                .catch(e => console.log(e));
            return await Promise.resolve('hello world');
        }

        fn()
        .then(v => console.log(v))
        // 出错了
        // hello world
        

        //例4 有多个await命令，可以统一放在try...catch结构中
        async function main() {
            try {
                const val1 = await firstStep();
                const val2 = await secondStep(val1);
                const val3 = await thirdStep(val1, val2);

                console.log('Final: ', val3);
            }
            catch (err) {
                console.error(err);
            }
        }
        //例5  探究await 命令
        //正常情况下，await命令后面是一个 Promise 对象。如果不是，会被转成一个立即resolve的 Promise 对象。
        async function f2() {
            await Promise.reject('出错了');
        }

        f2()
        .then(v => console.log(v))
        .catch(e => console.log(e))
        // 出错了 


        //例6  多个await命令后面的异步操作，如果不存在继发关系，最好让它们同时触发。

        // let fooPromise = getFoo();
        // let barPromise = getBar();
        // let foo = await fooPromise;
        // let bar = await barPromise;

        //例7  如果确实希望多个请求并发执行，可以使用Promise.all方法

         async function dbFuc(db) {
            let docs = [{}, {}, {}];
            let promises = docs.map((doc) => db.post(doc));

            let results = await Promise.all(promises);
            console.log(results);
        }

       //例8  Promise方法 注意事项

        //(1)-promise不能在forEach中执行，只能在for循环中执行

        async function test() {
            //这样不行
            /*arr.forEach(item => {
            await promiseFunc(item)
            })*/
            
            //这样可以
            for (var i = 0; i < arr.length; i++) {
                await promiseFunc(arr[i])
            }
        }
        //(2)-promise.map处理很大的数组（比如10000个元素）会导致内存占用很大

        async function test() {
            //arr有10000个元素，这样是不是会占用10000个单位的内存呢？
        await Promise.map(arr, promiseFunc)
            //arr有10000个元素，加上 {concurrency: 5}参数，这样可不可以把内存占用降到5个单位呢？
            await Promise.map(arr, promiseFunc, {concurrency: 5})
        }

    }
</script>
<body>
   <img style="margin-top:30px" src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1517820809119&di=91454088d1a6598a03ed5527086eeb41&imgtype=0&src=http%3A%2F%2Fm.qqzhi.com%2Fupload%2Fimg_2_1012068656D810419868_23.jpg"/>
   <span>(゜-゜)つロ 请在后台控制台查看....</span>
</body>
</html>